/***************************************************************************
 *   Copyright (C) 2010 by Peter Hatina                                    *
 *   email: phatina (at) gmail.com                                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License           *
 *   version 2.1 as published by the Free Software Foundation              *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Lesser General Public License for more details.                   *
 *   http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.               *
 ***************************************************************************/

#include "settings.h"
#include "common.h"
#include <QApplication>
#include <QSettings>
#include <QFile>
#include <QDir>
#include <QTextStream>
#include <QDomDocument>
#include <QDomNode>
#include <QtAlgorithms>
#include <QFileSystemWatcher>

#ifdef Q_OS_WIN32
#  include "winregistry.h"
#endif

#ifdef Q_OS_LINUX
#  include <cstdlib>
#endif

#if defined(Q_OS_LINUX)
const QString CONFIG_PREFIX = QDir::homePath() + "/.config/batterymeter" + "/";
#elif defined(Q_OS_WIN32)
const QString CONFIG_PREFIX = "";
#endif

const QString CONFIG_FILE = CONFIG_PREFIX + "batterymeter.conf";
const QString ACTION_FILE = CONFIG_PREFIX + "batterymeter.xml";

const QString KEY_ACTION_FILE = "action_file";
const QString KEY_UPDATE_INTERVAL = "update_interval";
const QString KEY_FIRST_RUN = "first_run";

#ifdef USE_LOGGING
const QString KEY_LOGGING = "log";
const QString KEY_LOG_FILE = "log_file";
const QString KEY_LOG_ENABLE = "log_enabled";
const QString DEF_LOG_FILE = "batterymeter.csv";
#endif

const int DEF_INTERVAL = 10;

const QString ACTION_ATTR = "attr";
const QString ACTION_TYPE = "type";
const QString ACTION_LEVEL = "level";

#if (defined(Q_OS_WIN32))
const char *RUN_PATH = "Software\\Microsoft\\Windows\\CurrentVersion\\Run";
#elif (defined(Q_OS_LINUX))
const QString RUN_DESKTOP_FILE = "batterymeter.desktop";
const QString RUN_LOCAL_PATH  = "/.config/autostart/";
const QString RUN_GLOBAL_PATH = "/etc/xdg/autostart/";
#endif

const int INDENT_SIZE = 4;

//------------------------------------------------------------------------------

QScopedPointer <Settings> Settings::m_instance;

Settings::Settings():
    m_settings(CONFIG_FILE, QSettings::IniFormat),
    m_actions_modified(false),
    m_actions_loaded(false)
{
}

Settings::~Settings()
{
    // save actions, if modified
    if (m_actions_modified)
        actionsSave();
}

Settings *Settings::instance()
{
    if (!m_instance)
    {
        // create new instance of settings, if necessary
        QScopedPointer <Settings> tmp(new Settings);
        m_instance.swap(tmp);
    }

    return m_instance.data();
}

#ifdef USE_LOGGING
void Settings::setLogging(bool log)
{
	m_logging = log;
}

void Settings::setLogFile(const QString &file)
{
    m_settings.setValue(KEY_LOG_FILE, file);
}
#endif

void Settings::setUpdateInterval(int interval)
{
    m_settings.setValue(KEY_UPDATE_INTERVAL, interval);
}

void Settings::setActionFile(const QString &file)
{
    m_settings.setValue(KEY_ACTION_FILE, file);
}

void Settings::setRunAtStartupGlobal(bool run)
{
    setRunAtStartUp(RUN_GLOBAL, run);
}

void Settings::setRunAtStartCurrent(bool run)
{
    setRunAtStartUp(RUN_LOCAL, run);
}

void Settings::setFirstRun(bool first)
{
    m_settings.setValue(KEY_FIRST_RUN, first);
}

void Settings::setRunAtStartUp(run_t type, bool run)
{
#if (defined(Q_OS_WIN32))
	HKEY root = (type == RUN_LOCAL) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;

	if (run)
	{
		QString entry = "\"" + QApplication::applicationFilePath().replace("/", "\\") + "\"";
		WindowsRegistry::createEntry(root, RUN_PATH,
			QApplication::applicationName().toStdString().c_str(),
			entry.toStdString().c_str());
	}
	else if (WindowsRegistry::entryExists(root, RUN_PATH,
		QApplication::applicationName().toStdString().c_str()))
	{
		WindowsRegistry::removeEntry(root, RUN_PATH,
			QApplication::applicationName().toStdString().c_str());
	}
#elif (defined(Q_OS_LINUX))
	QFile fout;
	QDir dir;

	if (type == RUN_LOCAL)
	{
		dir.setPath(getenv("HOME") + RUN_LOCAL_PATH);
		if (!dir.exists())
		{
			if (!dir.mkdir(dir.path()))
				return;
		}

		fout.setFileName(dir.path() + "/" + RUN_DESKTOP_FILE);
	}
	else
	{
		//TODO dokodit..
	}

	if (!run)
	{
		if (fout.exists())
			fout.remove();
	}
	else
	{
		if (!fout.open(QIODevice::WriteOnly))
			return;

		fout.write("[Desktop Entry]\n");
		fout.write("Type=Application\n");
		fout.write("Name=");
		fout.write(QApplication::applicationName().toStdString().c_str());
		fout.write("\nExec=");
		fout.write(QApplication::applicationFilePath().toStdString().c_str());
		fout.write("\nTerminal=false\n");
		fout.write("Hidden=false\n");
	}

	fout.close();
#endif
}

bool Settings::runAtStartUp(run_t type) const
{
#if (defined(Q_OS_WIN32))
	HKEY root = (type == RUN_LOCAL) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
	return WindowsRegistry::entryExists(root, RUN_PATH,
		QApplication::applicationName().toStdString().c_str());
#elif (defined(Q_OS_LINUX))
	QFile fin;

	if (type == RUN_LOCAL)
		fin.setFileName(getenv("HOME") + RUN_LOCAL_PATH + RUN_DESKTOP_FILE);
	else
		fin.setFileName(getenv("HOME") + RUN_GLOBAL_PATH + RUN_DESKTOP_FILE);

	return fin.exists();
#endif
}

#ifdef USE_LOGGING
bool Settings::log() const
{
    return m_logging;
}

QString Settings::logFile() const
{
    return m_log_file;
}

bool Settings::logEnabled() const
{
    return m_log_enabled;
}
#endif

int Settings::updateInterval() const
{
    return m_settings.value(KEY_UPDATE_INTERVAL, DEF_INTERVAL).toInt();
}

QString Settings::actionFile() const
{
    return m_settings.value(KEY_ACTION_FILE, ACTION_FILE).toString();
}

bool Settings::runAtStartUpGlobal() const
{
    return runAtStartUp(RUN_GLOBAL);
}

bool Settings::runAtStartUpCurrent() const
{
    return runAtStartUp(RUN_LOCAL);
}

bool Settings::firstRun() const
{
    return m_settings.value(KEY_FIRST_RUN, true).toBool();
}

int Settings::actionsCount() const
{
	return m_actions.count();
}

Action Settings::operator [] (int i) const
{
	if (i >= m_actions.count())
		throw IndexError();

	return m_actions[i];
}

void Settings::actionsClear()
{
    m_actions.clear();
    m_actions_modified = true;
}

QVector <Action> Settings::actions()
{
    // load actions, if necessary
    if (!m_actions_loaded)
    {
        actionsLoad();
        m_actions_loaded = true;
    }

    return m_actions;
}

void Settings::actionsAdd(Action action)
{
    m_actions.append(action);
    m_actions_modified = true;
}

void Settings::actionsLoad()
{
    // open input file
    QString xml_file = m_settings.value(KEY_ACTION_FILE, ACTION_FILE).toString();
    QDomDocument doc;
    QFile fin(xml_file);
    if (!fin.open(QIODevice::ReadOnly))
        return;

    if (!doc.setContent(&fin))
        return;

    fin.close();

    // read all the actions
    m_actions.clear();
    QDomNode root = doc.documentElement();
    QDomElement action_elem = root.firstChildElement();
    while (!action_elem.isNull())
    {
        Action action;
        action.setType(static_cast <Action::type_t> (action_elem.attribute(ACTION_TYPE, "2").toInt()));
        action.setLevel(action_elem.attribute(ACTION_LEVEL, "50").toInt());
        action.setAttr(action_elem.attribute(ACTION_ATTR, ""));
        action.setProcessed(false);

        // append, if suitable
        if (action.type() != Action::NOTHING)
            m_actions.append(action);

        action_elem = action_elem.nextSiblingElement();
    }

    qSort(m_actions);
}

void Settings::actionsSave()
{
    // create xml document
    QString xml_file = m_settings.value(KEY_ACTION_FILE, ACTION_FILE).toString();
    QDomDocument doc;
    doc.appendChild(doc.createElement("document"));

    // create xml document
    for (QVector <Action>::iterator it = m_actions.begin(); it != m_actions.end(); ++it)
    {
        QDomElement elem = doc.createElement("action");
        elem.setAttribute("type", it->type());
        elem.setAttribute("level", it->level());
        elem.setAttribute("attr", it->attr());
        QDomNode node = doc.documentElement();
        node.appendChild(elem);
    }

    // open file
    QFile fout(xml_file);
    if (!fout.open(QIODevice::WriteOnly))
        return;

    // save xml to file
    QTextStream out(&fout);
    doc.save(out, INDENT_SIZE);
    fout.close();

    // clear dirty flag
    m_actions_modified = false;
}

